import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Between, MoreThan, IsNull, LessThanOrEqual } from 'typeorm';
import { Visitor } from './entities/visitor.entity';
import { VisitorQueryDto } from './dto/visitor.dto';
import * as moment from 'moment';

@Injectable()
export class VisitorService {
  private onlineUsers: Set<string> = new Set(); // 存储在线用户的IP

  constructor(
    @InjectRepository(Visitor)
    private visitorRepository: Repository<Visitor>,
  ) {
    // 每5分钟清理超时的在线用户
    setInterval(() => this.cleanupOnlineUsers(), 5 * 60 * 1000);
  }

  async recordVisit(ip: string, location: string, device: string, source: string) {
    const visitor = new Visitor();
    visitor.ip = ip;
    visitor.location = location;
    visitor.device = device;
    visitor.source = source;
    visitor.visitTime = new Date();
    
    this.onlineUsers.add(ip);
    
    return this.visitorRepository.save(visitor);
  }

  async recordLeave(ip: string) {
    const visitor = await this.visitorRepository.findOne({
      where: { ip, leaveTime: IsNull() },
      order: { visitTime: 'DESC' },
    });

    if (visitor) {
      visitor.leaveTime = new Date();
      visitor.duration = Math.floor((visitor.leaveTime.getTime() - visitor.visitTime.getTime()) / 1000);
      await this.visitorRepository.save(visitor);
    }

    this.onlineUsers.delete(ip);
  }

  private cleanupOnlineUsers() {
    const fiveMinutesAgo = moment().subtract(5, 'minutes').toDate();
    this.visitorRepository.find({
      where: {
        visitTime: MoreThan(fiveMinutesAgo),
        leaveTime: IsNull(),
      },
    }).then(visitors => {
      const activeIps = visitors.map(v => v.ip);
      this.onlineUsers = new Set(activeIps);
    });
  }

  async getStats() {
    const today = moment().startOf('day').toDate();
    const [totalVisitors, todayVisitors, avgDuration] = await Promise.all([
      this.visitorRepository.count(),
      this.visitorRepository.count({ where: { visitTime: MoreThan(today) } }),
      this.visitorRepository
        .createQueryBuilder('visitor')
        .select('AVG(visitor.duration)', 'avgDuration')
        .where('visitor.duration > 0')
        .getRawOne(),
    ]);

    return {
      totalVisitors,
      todayVisitors,
      averageDuration: avgDuration?.avgDuration ? Math.round(avgDuration.avgDuration / 60 * 10) / 10 : 0, // 转换为分钟并保留一位小数
      onlineUsers: this.onlineUsers.size,
    };
  }

  async getTrend(days: number = 7) {
    const startDate = moment().subtract(days - 1, 'days').startOf('day').toDate();
    const visitors = await this.visitorRepository
      .createQueryBuilder('visitor')
      .select('DATE(visitor.visitTime)', 'date')
      .addSelect('COUNT(*)', 'count')
      .where('visitor.visitTime >= :startDate', { startDate })
      .groupBy('DATE(visitor.visitTime)')
      .getRawMany();

    return visitors.map(v => ({
      date: v.date,
      count: parseInt(v.count),
    }));
  }

  async getSourceStats() {
    const result = await this.visitorRepository
      .createQueryBuilder('visitor')
      .select('visitor.source', 'source')
      .addSelect('COUNT(*)', 'count')
      .groupBy('visitor.source')
      .getRawMany();

    return result.map(item => ({
      source: item.source,
      count: parseInt(item.count),
    }));
  }

  async getRecords(query: VisitorQueryDto) {
    const { startDate, endDate, page = 1, limit = 10 } = query;
    const queryBuilder = this.visitorRepository.createQueryBuilder('visitor');

    // 如果有开始日期
    if (startDate) {
      queryBuilder.where('visitor.visitTime >= :startDate', {
        startDate: moment(startDate).startOf('day').toDate(),
      });
    }

    // 如果有结束日期
    if (endDate) {
      queryBuilder.andWhere('visitor.visitTime <= :endDate', {
        endDate: moment(endDate).endOf('day').toDate(),
      });
    }

    // 添加排序、分页
    queryBuilder
      .orderBy('visitor.visitTime', 'DESC')
      .skip((page - 1) * limit)
      .take(limit);

    // 执行查询
    const [items, total] = await queryBuilder.getManyAndCount();

    return {
      items,
      total,
      page,
      limit,
    };
  }
} 